/********************************************
 * 功能说明: 
 * 模块名称: 
 * 系统名称: 
 * 软件版权: 北京银杉金服科技有限公司
 * 系统版本: 1.0.0
 * 开发人员: zhangfb
 * 开发时间: 2018/9/8 14:45
 * 审核人员: 
 * 相关文档: 
 * 修改记录: 修改日期 修改人员 修改说明
 *********************************************/

package com.hyacinth.config;

import com.hyacinth.security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * @author zhangfb
 * @version 1.0.0.1
 * @since JDK 1.8
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

    private final PasswordEncoder passwordEncoder;
    private final AccessDecisionManager accessDecisionManager;
    private final CustomAuthSuccessHandler customAuthSuccessHandler;
    private final CustomAuthFailureHandler customAuthFailureHandler;
    private final CustomUserDetailsService customUserDetailsService;
    private final CustomAccessDeniedHandler customAccessDeniedHandler;
    private final CustomLogoutSuccessHandler customLogoutSuccessHandler;
    private final CustomSecurityMetadataSource customSecurityMetadataSource;
    private final CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    @Autowired
    public WebSecurityConfig(PasswordEncoder passwordEncoder,
                             AccessDecisionManager accessDecisionManager,
                             CustomAuthSuccessHandler customAuthSuccessHandler,
                             CustomAuthFailureHandler customAuthFailureHandler,
                             CustomUserDetailsService customUserDetailsService,
                             CustomAccessDeniedHandler customAccessDeniedHandler,
                             CustomLogoutSuccessHandler customLogoutSuccessHandler,
                             CustomSecurityMetadataSource customSecurityMetadataSource,
                             CustomAuthenticationEntryPoint customAuthenticationEntryPoint) {
        this.passwordEncoder = passwordEncoder;
        this.accessDecisionManager = accessDecisionManager;
        this.customAuthSuccessHandler   = customAuthSuccessHandler;
        this.customAuthFailureHandler   = customAuthFailureHandler;
        this.customUserDetailsService   = customUserDetailsService;
        this.customAccessDeniedHandler = customAccessDeniedHandler;
        this.customAuthenticationEntryPoint = customAuthenticationEntryPoint;
        this.customLogoutSuccessHandler = customLogoutSuccessHandler;
        this.customSecurityMetadataSource = customSecurityMetadataSource;
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        //指定密码加密器，需要将密码加密后写入数据库
        auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder);
        //不删除凭据，以便记住用户
        auth.eraseCredentials(false);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests() //请求鉴权
                // 不需要权限验证的请求前缀
                .antMatchers("/login","/js/**", "/vendor/**", "/resource/**", "/oauth/**").permitAll()
                .anyRequest().authenticated()    // 其他请求都需要验证
                .accessDecisionManager(accessDecisionManager) //设置投票管理器
                //设置前置请求（加载资源权限列表）
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    public <O extends FilterSecurityInterceptor> O postProcess(O fsi) {
                        fsi.setSecurityMetadataSource(customSecurityMetadataSource);
                        return fsi;
                    }
                })
//                .antMatchers("/hello")
//                .hasRole("ADMIN")
//                .hasAuthority("ADMIN") //登陆后之后拥有“ADMIN”权限才可以访问/hello方法，否则系统会出现“403”权限不足的提示
                .and()
            .exceptionHandling() //异常处理
                .accessDeniedHandler(customAccessDeniedHandler) //自定义访问失败处理器
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                .and()
            .addFilter(new CustomAuthorizationFilter(authenticationManager())) //token授权过滤
//            .addFilter() //token鉴权
            .formLogin() //form表单登陆
                .loginPage("/login")            //自定义登录页url,默认为/login
                .failureUrl("/login?error=1")   //默认登录失败页面
                .loginProcessingUrl("/j_login") //登录请求拦截的url,也就是form表单提交时指定的action
//                .defaultSuccessUrl("/index")    //默认登录成功后跳转的url
//                .successHandler(customAuthSuccessHandler)
//                .failureHandler(customAuthFailureHandler)
                .permitAll()
                .and()
            .logout() //登出
                .logoutUrl("/logout")
//                .logoutSuccessUrl("/login")     //退出登录后/login
                .permitAll()
                .logoutSuccessHandler(customLogoutSuccessHandler)  //登出成功处理
                .invalidateHttpSession(true);   //session失效
//                .and()
//            .rememberMe()//登录后记住用户，下次自动登录,数据库中必须存在名为persistent_logins的表
//                .tokenValiditySeconds(1209600) //指定令牌的有效时间,单位秒
//                .and()
//                //session管理
//            .sessionManagement()
//                //session失效后跳转
//                .invalidSessionUrl("/login")
//                //只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面
//                .maximumSessions(1)
//                .expiredUrl("/login");
    }

//    //不定义没有password grant_type
//    @Override
//    @Bean
//    public AuthenticationManager authenticationManagerBean() throws Exception {
//        return super.authenticationManagerBean();
//    }

}
